package com.xzzz.irda.guardcat.core.metric.gc;

import cn.hutool.core.collection.CollectionUtil;
import com.sun.management.GarbageCollectorMXBean;
import com.sun.management.GcInfo;
import com.xzzz.irda.guardcat.core.metric.memory.BaseMemoryUsageInfo;
import com.xzzz.irda.guardcat.core.metric.memory.UsageInfo;

import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import java.lang.management.RuntimeMXBean;
import java.util.*;

/**
 * 垃圾收集器信息
 * -XX:+UseParallelGC -XX:+UseParallelOldGC -Xss512K -Xms1408M -Xmx1408M -XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:MetaspaceSize=384M -XX:MaxMetaspaceSize=384M -XX:MaxDirectMemorySize=256M
 *
 * @author xzzz
 * @since 1.0.0
 */
public class GarbageCollectorInfo implements Serializable {
    private static final long serialVersionUID = 1L;

    private static final String CODE_CACHE = "Code Cache";
    private static final String CODE_CACHE_MEMORY = "JIT 编译代码缓存区内存";

    private static final String METASPACE = "Metaspace";
    private static final String METASPACE_MEMORY = "元空间内存";

    private static final String COMPRESSED_CLASS_SPACE = "Compressed Class Space";
    private static final String COMPRESSED_CLASS_SPACE_MEMORY = "类压缩数据区";

    private static final String EDEN_SPACE = "Eden Space";
    private static final String EDEN_SPACE_MEMORY = "新生代";

    private static final String SURVIVOR_SPACE = "Survivor Space";
    private static final String SURVIVOR_SPACE_MEMORY = "幸存代";

    private static final String OLD_GEN = "Old Gen";
    private static final String OLD_GEN_MEMORY = "老年代";

    /**
     * 垃圾收集器名称
     */
    private String name;

    /**
     * 垃圾收集器总的 GC 次数
     */
    private long totalGcCount;

    /**
     * 垃圾收集器总的 GC 时间【单位：毫秒】
     */
    private long totalGcTime;

    /**
     * 最后一次 GC 信息
     */
    private LastGcInfo lastGcInfo;

    /**
     * 获取所有垃圾收集器信息【触发 GC 之后才会有信息】
     *
     * @return List<GarbageCollectorInfo>
     */
    public static List<GarbageCollectorInfo> getGarbageCollectorInfos() {

        List<GarbageCollectorInfo> garbageCollectorInfoList = new ArrayList<>();

        RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
        // JVM 启动时间
        long jvmStartupTime = runtimeMxBean.getStartTime();

        List<java.lang.management.GarbageCollectorMXBean> garbageCollectorMxBeanList = ManagementFactory.getGarbageCollectorMXBeans();
        if (CollectionUtil.isNotEmpty(garbageCollectorMxBeanList)) {
            GarbageCollectorMXBean gcMxBean;
            GarbageCollectorInfo garbageCollectorInfo;
            GcInfo gcInfo;

            GcMemoryUsageInfo beforeGc = null;
            GcMemoryUsageInfo afterGc = null;

            for (java.lang.management.GarbageCollectorMXBean garbageCollectorMxBean : garbageCollectorMxBeanList) {
                gcMxBean = (GarbageCollectorMXBean) garbageCollectorMxBean;
                garbageCollectorInfo = new GarbageCollectorInfo();
                garbageCollectorInfo.name = gcMxBean.getName();
                garbageCollectorInfo.totalGcCount = gcMxBean.getCollectionCount();
                garbageCollectorInfo.totalGcTime = gcMxBean.getCollectionTime();

                gcInfo = gcMxBean.getLastGcInfo();
                if (null == gcInfo) {
                    continue;
                }

                if (null != gcInfo.getMemoryUsageBeforeGc()) {
                    beforeGc = getGcMemoryUsageInfo(gcInfo.getMemoryUsageBeforeGc().entrySet());
                }
                if (null != gcInfo.getMemoryUsageAfterGc()) {
                    afterGc = getGcMemoryUsageInfo(gcInfo.getMemoryUsageAfterGc().entrySet());
                }

                // gcInfo.getStartTime() ：自Java虚拟机启动以来此GC的开始时间（以毫秒为单位）。
                // gcInfo.getEndTime() ：自Java虚拟机启动以来此GC的结束时间（以毫秒为单位）。
                // gcInfo.getDuration() ：以毫秒为单位返回此GC的已用时间。
                garbageCollectorInfo.lastGcInfo = new LastGcInfo(gcInfo.getId(), new Date(jvmStartupTime + gcInfo.getStartTime()), new Date(jvmStartupTime + gcInfo.getEndTime()), gcInfo.getDuration(), beforeGc, afterGc);

                garbageCollectorInfoList.add(garbageCollectorInfo);
            }
        }

        return garbageCollectorInfoList;
    }

    private static GcMemoryUsageInfo getGcMemoryUsageInfo(Set<Map.Entry<String, MemoryUsage>> set) {
        if (CollectionUtil.isEmpty(set)) {
            return null;
        }
        String key;
        MemoryUsage memoryUsage;
        UsageInfo usageInfo;
        BaseMemoryUsageInfo codeCacheUsage = null;
        BaseMemoryUsageInfo metaspaceUsage = null;
        BaseMemoryUsageInfo compressedClassSpaceUsage = null;
        BaseMemoryUsageInfo edenSpaceUsage = null;
        BaseMemoryUsageInfo survivorSpaceUsage = null;
        BaseMemoryUsageInfo oldGenUsage = null;
        for (Map.Entry<String, MemoryUsage> map : set) {
            key = map.getKey();
            if (null == key) {
                continue;
            }

            memoryUsage = map.getValue();
            usageInfo = new UsageInfo(memoryUsage.getInit(), memoryUsage.getMax(), memoryUsage.getCommitted(), memoryUsage.getUsed());

            if (key.contains(CODE_CACHE)) {
                codeCacheUsage = new BaseMemoryUsageInfo(CODE_CACHE_MEMORY, usageInfo);
            }
            if (key.contains(METASPACE)) {
                metaspaceUsage = new BaseMemoryUsageInfo(METASPACE_MEMORY, usageInfo);
            }
            if (key.contains(COMPRESSED_CLASS_SPACE)) {
                compressedClassSpaceUsage = new BaseMemoryUsageInfo(COMPRESSED_CLASS_SPACE_MEMORY, usageInfo);
            }
            if (key.contains(EDEN_SPACE)) {
                edenSpaceUsage = new BaseMemoryUsageInfo(EDEN_SPACE_MEMORY, usageInfo);
            }
            if (key.contains(SURVIVOR_SPACE)) {
                survivorSpaceUsage = new BaseMemoryUsageInfo(SURVIVOR_SPACE_MEMORY, usageInfo);
            }
            if (key.contains(OLD_GEN)) {
                oldGenUsage = new BaseMemoryUsageInfo(OLD_GEN_MEMORY, usageInfo);
            }
        }

        return new GcMemoryUsageInfo(codeCacheUsage, metaspaceUsage, compressedClassSpaceUsage, edenSpaceUsage, survivorSpaceUsage, oldGenUsage);
    }

    private GarbageCollectorInfo() {
    }

    public String getName() {
        return name;
    }

    public long getTotalGcCount() {
        return totalGcCount;
    }

    public long getTotalGcTime() {
        return totalGcTime;
    }

    public LastGcInfo getLastGcInfo() {
        return lastGcInfo;
    }

    @Override
    public String toString() {
        return "垃圾收集器的信息{" +
                "名称='" + name + '\'' +
                ", 总的 GC 次数=" + totalGcCount +
                ", 总的 GC 时间=" + totalGcTime + "毫秒" +
                ", 最后一次 GC 信息=" + lastGcInfo +
                '}';
    }

    public static void main(String[] args) {
        byte[] byteArr = new byte[8192];

        System.gc();

        byte[] byteArr1 = new byte[8192];

        System.gc();

        System.out.println(getGarbageCollectorInfos());

        System.out.println(393216 / 1024);
    }
}
